package com.cake.liulanxiangzhu.security;

import com.alibaba.fastjson.JSON;
import com.cake.liulanxiangzhu.web.JsonResult;
import com.cake.liulanxiangzhu.web.ServiceCode;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.cake.liulanxiangzhu.service.impl.AdminUserServiceImpl.LOGINUSER;

@Component
@Slf4j
public class JwtAuthorizationFilter extends OncePerRequestFilter {
    public static final int JWT_MIN_LENGTH = 100;

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String requestPath = request.getRequestURI();

        // 清除SecurityContext中原有的数据（认证信息）
        SecurityContextHolder.clearContext();

        // 设置响应的文档类型
        response.setContentType("application/json; charset=utf-8");

        // 尝试获取客户端提交请求时可能携带的JWT
        String jwt = request.getHeader("Authorization");
        log.debug("接收到JWT数据：{}", jwt);

        // 判断是否获取到有效的JWT
        if (!StringUtils.hasText(jwt) || jwt.length() < JWT_MIN_LENGTH) {
             //直接放行
            if (requestPath.equals("/user/login")||requestPath.equals("/user/smsLogin")
                    ||requestPath.equals("/user/getRandomNum")||requestPath.equals("/user/weiXinLogin")){
                log.info("登录未获取到有效的JWT数据，将直接放行");
                filterChain.doFilter(request, response);
                return;
            }else {
                JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_FORBIDDEN, "未登录的非法访问");
                String jsonResultString = JSON.toJSONString(jsonResult);
                PrintWriter writer = response.getWriter();
                writer.println(jsonResultString);
                writer.close();
                return;
            }
        }

        String secretKey = "liulanxiangzhushidacake";
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
        } catch (SignatureException e) {
            String message = "非法访问！";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_SIGNATURE, message);
            String jsonResultString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonResultString);
            writer.close();
            return;
        } catch (MalformedJwtException e) {
            String message = "非法访问！";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_MALFORMED, message);
            String jsonResultString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonResultString);
            writer.close();
            return;
        } catch (ExpiredJwtException e) {
            String message = "登录已过期，请重新登录！";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED, message);
            String jsonResultString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonResultString);
            writer.close();
            return;
        } catch (Throwable e) {
            e.printStackTrace(); // 重要
            String message = "服务器忙，请稍后再次尝试！";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_UNKNOWN, message);
            String jsonResultString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonResultString);
            writer.close();
            return;
        }

        Integer id = claims.get("id", Integer.class);
        String username = claims.get("username", String.class);
        String uuid = "\"" + claims.get("uuid", String.class)+"\"";
        String authorities = claims.get("authorities", String.class);
        List<SimpleGrantedAuthority> authoritieList = JSON.parseArray(authorities, SimpleGrantedAuthority.class);


        if (redisTemplate.hasKey(LOGINUSER + id)){
            String sUuid = (String) redisTemplate.opsForValue().get(LOGINUSER + id);
            if (sUuid.equals(uuid)){
                LoginPrincipal loginPrincipal = new LoginPrincipal();
                loginPrincipal.setUsername(username);
                loginPrincipal.setId(id);
                Authentication authentication = new UsernamePasswordAuthenticationToken(
                        loginPrincipal, null, authoritieList);
                redisTemplate.opsForValue().set(LOGINUSER + id, JSON.toJSONString(claims.get("uuid", String.class)), 30, TimeUnit.MINUTES);
                SecurityContext securityContext = SecurityContextHolder.getContext();
                securityContext.setAuthentication(authentication);
                filterChain.doFilter(request,response);
            }else {
                JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_BAD_REQUEST, "未认证，请登录");
                String jsonResultString = JSON.toJSONString(jsonResult);
                PrintWriter writer = response.getWriter();
                writer.println(jsonResultString);
                writer.close();
            }
        }else {
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_BAD_REQUEST, "未认证，请登录");
            String jsonResultString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonResultString);
            writer.close();
        }


    }

}
